// Fill out your copyright notice in the Description page of Project Settings.


#include "Input/InputConfigureLibrary.h"
#include "GenericPlatform/GenericPlatformFile.h"
#include "Misc/FileHelper.h"
#include "Interfaces/IPluginManager.h"
#include "Kismet/KismetStringLibrary.h"
#include "WGIUserWidget.h"
#include "Input/SGDefaultWGIInput.h"
#include "Kismet/GameplayStatics.h"


DEFINE_LOG_CATEGORY(WGI_ConfigFileLog);



FActionMappingContainers::FActionMappingContainers()
{
	Content.Empty();
}

FInputConfigureGroupData::FInputConfigureGroupData()
{
	ResetData();
}

FInputConfigureGroupData::FInputConfigureGroupData(FString inGroupKey)
{
	GroupKey = inGroupKey;
	LoadDataAtConfigFile();
}

void FInputConfigureGroupData::LoadDataAtConfigFile()
{
	TArray<FString> Contents;
	if (InputConfigureLibrary::LoadInputConfigFile(FString(InputConfigureLibrary::GetConfigFileDir() + GroupKey + ".ini"), Contents))
	{
		Contents.RemoveAt(0);

		TArray<FString> FixedInputContents; FixedInputContents.Empty();
		TArray<FString> OtherInputContents; OtherInputContents.Empty();

		bool AddToOtherInput = false;
		for (size_t i = 0; i < Contents.Num(); i++)
		{
			if (AddToOtherInput)
			{
				OtherInputContents.Add(Contents[i]);
			}
			else
			{
				if (Contents[i] == FString("OtherInputSettings::"))
				{
					AddToOtherInput = true;
					continue;
				}
				FixedInputContents.Add(Contents[i]);
			}
		}

		LoadFixedInputAtStringArray(FixedInputContents);
		LoadOtherInputAtStringArray(OtherInputContents);

		ChangedInputGroup.ExecuteIfBound(GroupKey);

		for (size_t i = 0; i < OpenedWGIWidget.Num(); i++)
		{
			OpenedWGIWidget[i]->OnReloadConfigEvent();
		}
	}
	else
	{
		UE_LOG(WGI_ConfigFileLog, Error, TEXT("load input config file failure ! --- %s"), *GroupKey);
	}
}

void FInputConfigureGroupData::LoadDataAtConfigFile(FString inNewGroup)
{
	GroupKey = inNewGroup;
#if WITH_EDITOR
	InputConfigureLibrary::SyncUpdateCode();
#endif
	LoadDataAtConfigFile();
}

void FInputConfigureGroupData::LoadFixedInputAtStringArray(const TArray<FString>& inContents)
{
	InputConfigureLibrary::StringToActions(EnterAction, inContents[0]);
	InputConfigureLibrary::StringToActions(BackAction, inContents[1]);
}

void FInputConfigureGroupData::LoadOtherInputAtStringArray(const TArray<FString>& inContents)
{
	OtherActions.Empty();
	for (size_t i = 0; i < inContents.Num(); i++)
	{
		TArray<FString> ContentArray; ContentArray.Empty();
		inContents[i].ParseIntoArray(ContentArray, *FString("]["), false);

		FName ActionTag = UKismetStringLibrary::Conv_StringToName(ContentArray[0]);

		FActionMappingContainers ActionInput = FActionMappingContainers();
		InputConfigureLibrary::StringToActions(ActionInput.Content, ContentArray[1]);

		OtherActions.Add(ActionTag, ActionInput);
	}
}

void FInputConfigureGroupData::ConvertToStringArray(TArray<FString>& OutArray)
{
	OutArray.Empty();

	TArray<FString> FixedInputContents;
	ConvertFixedInputToStringArray(FixedInputContents);
	TArray<FString> OtherInputContents;
	ConvertOtherInputToStringArray(OtherInputContents);

	OutArray.Add(FString("FixedInputSettings::"));
	OutArray.Append(FixedInputContents);
	OutArray.Add(FString("OtherInputSettings::"));
	OutArray.Append(OtherInputContents);
}

void FInputConfigureGroupData::ConvertFixedInputToStringArray(TArray<FString>& OutArray)
{
	OutArray.Empty();

	OutArray.Add(InputConfigureLibrary::ActionsToString(EnterAction));
	OutArray.Add(InputConfigureLibrary::ActionsToString(BackAction));
}

void FInputConfigureGroupData::ConvertOtherInputToStringArray(TArray<FString>& OutArray)
{
	OutArray.Empty();
	TArray<FName> Tags;
	OtherActions.GenerateKeyArray(Tags);

	for (size_t i = 0; i < Tags.Num(); i++)
	{
		const FName& Tag = Tags[i];
		const TArray<FInputActionKeyMapping>& Input = OtherActions.Find(Tag)->Content;

		FString Content = Tag.ToString();
		Content.Append("][");
		Content.Append(InputConfigureLibrary::ActionsToString(Input));

		OutArray.Add(Content);
	}
}

void FInputConfigureGroupData::ResetData()
{
	//GroupKey = FString("None");
	EnterAction.Empty();
	BackAction.Empty();

	OtherActions.Empty();
}

void FInputConfigureGroupData::BindOnConfigReload(UWGI_UserWidget* inWidget)
{
	if (!OpenedWGIWidget.Contains(inWidget))
	{
		OpenedWGIWidget.Add(inWidget);
	}
}

void FInputConfigureGroupData::UnBindOnConfigReload(UWGI_UserWidget* inWidget)
{
	if (OpenedWGIWidget.Contains(inWidget))
	{
		OpenedWGIWidget.Remove(inWidget);
	}
}

void FInputConfigureGroupData::BindChangedInputGroupEvent(FOnChangedInputGroup inChangedInputGroup)
{
	ChangedInputGroup.Clear();
	ChangedInputGroup = inChangedInputGroup;
}

void FInputConfigureGroupData::BindAnyKeyDownEvent(FOnAnyKeyDown inAnyKeyDownEvent)
{
	AnyKeyDownEvent.Clear();
	AnyKeyDownEvent = inAnyKeyDownEvent;
}

void FInputConfigureGroupData::CallAnyKeyDownEvent(const FKeyEvent& InKeyEvent, bool bIsDown)
{
	AnyKeyDownEvent.ExecuteIfBound(InKeyEvent, bIsDown);
}




void InputConfigureLibrary::CreateDefaultData(FInputConfigureGroupData& OutData)
{
	OutData.ResetData();
	OutData.EnterAction.Add(FInputActionKeyMapping(NAME_None, FKey(EKeys::SpaceBar)));
	OutData.BackAction.Add(FInputActionKeyMapping(NAME_None, FKey(EKeys::BackSpace)));
}

FString InputConfigureLibrary::GetConfigFileDir()
{
	return FPaths::ProjectConfigDir() + TEXT("InputConfigure/");
}

FString InputConfigureLibrary::GetPluginBaseDir()
{
	return IPluginManager::Get().FindPlugin("WidgetGroupInput")->GetBaseDir();
}

void InputConfigureLibrary::FindInputConfigFiles(TArray<FString>& OutFiles)
{
	OutFiles.Empty();
	FString fileDic = GetConfigFileDir();
	IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
	if (PlatformFile.DirectoryExists(*fileDic))
	{
		PlatformFile.FindFiles(OutFiles, *fileDic, *FString(".ini"));

		for (size_t i = 0; i < OutFiles.Num(); i++)
		{
			if (OutFiles[i].Contains(FString("DefaultLoadGroupName.ini")))
			{
				OutFiles.RemoveAt(i);
				return;
			}
		}

	}
	else
	{
		PlatformFile.CreateDirectoryTree(*fileDic);
	}
}

bool InputConfigureLibrary::HasAnyInputConfigFile()
{
	TArray<FString> Files;
	FindInputConfigFiles(Files);
	return Files.IsValidIndex(0);
}

bool InputConfigureLibrary::LoadInputConfigFile(const FString filename, TArray<FString>& OutArray)
{
	OutArray.Empty();
	return FFileHelper::LoadFileToStringArray(OutArray, *filename);
}

FString InputConfigureLibrary::ActionsToString(const TArray<FInputActionKeyMapping>& inActions)
{
	FString ActionStr = FString();
	for (size_t i = 0; i < inActions.Num(); i++)
	{
		ActionStr.Append(ActionToString(inActions[i]));

		if (i != inActions.Num() - 1)
		{
			ActionStr.Append("|");
		}
	}
	return ActionStr;
}

FString InputConfigureLibrary::ActionToString(const FInputActionKeyMapping& inAction)
{
	FString ActionStr = FString();
	ActionStr.Append(inAction.Key.ToString());
	ActionStr.Append("*");
	ActionStr.Append(UKismetStringLibrary::Conv_BoolToString(inAction.bAlt));
	ActionStr.Append("*");
	ActionStr.Append(UKismetStringLibrary::Conv_BoolToString(inAction.bCtrl));
	ActionStr.Append("*");
	ActionStr.Append(UKismetStringLibrary::Conv_BoolToString(inAction.bShift));
	ActionStr.Append("*");
	ActionStr.Append(UKismetStringLibrary::Conv_BoolToString(inAction.bCmd));

	return ActionStr;
}

void InputConfigureLibrary::StringToActions(TArray<FInputActionKeyMapping>& OutActions, const FString& ActionsStr)
{
	OutActions.Empty();
	TArray<FString> AllInputStrArray;
	ActionsStr.ParseIntoArray(AllInputStrArray, *FString("|"), false);

	for (size_t i = 0; i < AllInputStrArray.Num(); i++)
	{
		FInputActionKeyMapping Action = FInputActionKeyMapping();
		StringToAction(Action, AllInputStrArray[i]);
		OutActions.Add(Action);
	}
}

void InputConfigureLibrary::StringToAction(FInputActionKeyMapping& OutAction, const FString& ActionStr)
{
	TArray<FString> ActionContent;
	ActionStr.ParseIntoArray(ActionContent, *FString("*"), false);

	OutAction.Key = FKey(UKismetStringLibrary::Conv_StringToName(ActionContent[0]));
	OutAction.bAlt = ActionContent[1].ToBool();
	OutAction.bCtrl = ActionContent[2].ToBool();
	OutAction.bShift = ActionContent[3].ToBool();
	OutAction.bCmd = ActionContent[4].ToBool();
}

FString InputConfigureLibrary::GetDefaultConfigGroupName()
{
#if WITH_EDITOR
	{
		FString fileDic = GetConfigFileDir();
		FString Result = FString("");
		if (FFileHelper::LoadFileToString(Result, *FString(fileDic + "/DefaultLoadGroupName.ini")))
		{
			IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
			if (PlatformFile.FileExists(*FString(InputConfigureLibrary::GetConfigFileDir() + Result + ".ini")))
			{
				return Result;
			}
		}

		TArray<FString> files;
		FindInputConfigFiles(files);
		if (files.IsValidIndex(0))
		{
			FString Name = FPaths::GetBaseFilename(files[0]);
			SetDefaultConfigGroupName(Name);
			return Name;
		}
		UE_LOG(WGI_ConfigFileLog, Error, TEXT("Not has any Input config file"));
		return FString("None");
	}
#endif

	USG_DefaultWGIInput* SG = Cast<USG_DefaultWGIInput>(UGameplayStatics::LoadGameFromSlot(FString("DefaultWGIInput"), 0));
	if (SG)
	{
		IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
		if (PlatformFile.FileExists(*FString(InputConfigureLibrary::GetConfigFileDir() + SG->DefaultLoadGroupName + ".ini")))
		{
			return SG->DefaultLoadGroupName;
		}
	}
	UE_LOG(WGI_ConfigFileLog, Error, TEXT("Load SG Default WGI Input Faild"));

	FString fileDic = GetConfigFileDir();
	FString Result = FString("");
	if (FFileHelper::LoadFileToString(Result, *FString(fileDic + "/DefaultLoadGroupName.ini")))
	{
		IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
		if (PlatformFile.FileExists(*FString(InputConfigureLibrary::GetConfigFileDir() + Result + ".ini")))
		{
			return Result;
		}
	}

	TArray<FString> files;
	FindInputConfigFiles(files);
	if (files.IsValidIndex(0))
	{
		FString Name = FPaths::GetBaseFilename(files[0]);
		SetDefaultConfigGroupName(Name);
		return Name;
	}
	UE_LOG(WGI_ConfigFileLog, Error, TEXT("Not has any Input config file"));
	return FString("None");

}

void InputConfigureLibrary::SetDefaultConfigGroupName(FString GroupName)
{
#if WITH_EDITOR
	FFileHelper::SaveStringToFile(GroupName, *FString(GetConfigFileDir() + "/DefaultLoadGroupName.ini"));
	return;
#endif

	USG_DefaultWGIInput* SG = Cast<USG_DefaultWGIInput>(UGameplayStatics::CreateSaveGameObject(USG_DefaultWGIInput::StaticClass()));
	if (SG)
	{
		SG->DefaultLoadGroupName = GroupName;

		if (UGameplayStatics::SaveGameToSlot(SG, FString("DefaultWGIInput"), 0))
			return;
	}
	UE_LOG(WGI_ConfigFileLog, Error, TEXT("Save SG Default WGI Input Faild"));

}




#if WITH_EDITOR 

FString InputConfigureLibrary::UpdateCode = FString("Temp UnKnow");

void InputConfigureLibrary::ChackUpdateCode()
{
	FString NewUpdateCode = FString("");
	FString fileDir = FString(GetPluginBaseDir() + "/Saved/UpdateCode/");
	if (FFileHelper::LoadFileToString(NewUpdateCode, *FString(fileDir + InputConfigureGroupDataCache->GroupKey)))
	{
		if (NewUpdateCode == UpdateCode)
		{
			return;
		}
		else
		{
			UpdateCode = NewUpdateCode;
			InputConfigureGroupDataCache->ResetData();
			InputConfigureGroupDataCache->LoadDataAtConfigFile();
			return;
		}
	}

	UE_LOG(WGI_ConfigFileLog, Warning, TEXT("Editor Not Find This Group UpdateCodeCache file. Use Default Group . --- %s"), *FString(fileDir + InputConfigureGroupDataCache->GroupKey));
	InputConfigureGroupDataCache->ResetData();
	InputConfigureGroupDataCache->LoadDataAtConfigFile(GetDefaultConfigGroupName());
	SyncUpdateCode();
	return;
}

void InputConfigureLibrary::SyncUpdateCode()
{
	if (InputConfigureGroupDataCache)
	{
		FString fileDir = FString(GetPluginBaseDir() + "/Saved/UpdateCode/");
		FFileHelper::LoadFileToString(UpdateCode, *FString(fileDir + InputConfigureGroupDataCache->GroupKey));
	}
}

#endif

FInputConfigureGroupData* InputConfigureLibrary::InputConfigureGroupDataCache = nullptr;

void InputConfigureLibrary::EnsureDataCacheValid(bool CheckUpdate)
{
	if (InputConfigureGroupDataCache)
	{
		if (CheckUpdate)
		{
			if (InputConfigureGroupDataCache->GroupKey == TEXT("None"))
			{
				InputConfigureGroupDataCache->ResetData();
				InputConfigureGroupDataCache->LoadDataAtConfigFile(GetDefaultConfigGroupName());
#if WITH_EDITOR
				SyncUpdateCode();
#endif
				return;
			}
#if WITH_EDITOR 
			ChackUpdateCode();
#endif
		}
	}
	else
	{
		InputConfigureGroupDataCache = new FInputConfigureGroupData(GetDefaultConfigGroupName());
#if WITH_EDITOR
		SyncUpdateCode();
#endif
	}
}

void InputConfigureLibrary::ReloadInputConfig(FString GroupName)
{
	EnsureDataCacheValid(false);
	InputConfigureGroupDataCache->ResetData();
	InputConfigureGroupDataCache->LoadDataAtConfigFile(GroupName);
}

FString InputConfigureLibrary::GetCurrentInputConfigName()
{
	EnsureDataCacheValid(false);
	return InputConfigureGroupDataCache->GroupKey;
}

void InputConfigureLibrary::CheckReloadToDefaultInputConfig()
{
#if WITH_EDITOR
	EnsureDataCacheValid(false);

	FString Default = GetDefaultConfigGroupName();
	if (InputConfigureGroupDataCache->GroupKey != Default)
	{
		InputConfigureGroupDataCache->ResetData();
		InputConfigureGroupDataCache->LoadDataAtConfigFile(Default);
	}
#endif
}

void InputConfigureLibrary::BindOnConfigReloadEvent(UWGI_UserWidget* inWidget)
{
	EnsureDataCacheValid(false);
	InputConfigureGroupDataCache->BindOnConfigReload(inWidget);
}

void InputConfigureLibrary::UnBindOnConfigReloadEvent(UWGI_UserWidget* inWidget)
{
	EnsureDataCacheValid(false);
	InputConfigureGroupDataCache->UnBindOnConfigReload(inWidget);
}

bool InputConfigureLibrary::GetOtherInputKeysFromKeyName(FName inKeyName, TArray<FInputActionKeyMapping>*& OutKeys)
{
	if (inKeyName != NAME_None)
	{
		EnsureDataCacheValid();
		if (InputConfigureGroupDataCache->OtherActions.Contains(inKeyName))
		{
			OutKeys = &(InputConfigureGroupDataCache->OtherActions.Find(inKeyName)->Content);
			return true;
		}

		UE_LOG(WGI_ConfigFileLog, Error, TEXT("The key name was not found! --- %s"), *inKeyName.ToString());
	}
	else
	{
		UE_LOG(WGI_ConfigFileLog, Error, TEXT("Find Key Name Cannot is None !"));
	}
	OutKeys = nullptr;
	return false;
}

bool InputConfigureLibrary::ThisIsEnterKey(const FKeyEvent& InKeyEvent)
{
	EnsureDataCacheValid();
	const FInputActionKeyMapping Mapping = FInputActionKeyMapping(NAME_None, InKeyEvent.GetKey(),
		InKeyEvent.IsShiftDown(), InKeyEvent.IsControlDown(), InKeyEvent.IsAltDown(), InKeyEvent.IsCommandDown());

	return InputConfigureGroupDataCache->EnterAction.Contains(Mapping);
}

bool InputConfigureLibrary::ThisIsBackKey(const FKeyEvent& InKeyEvent)
{
	EnsureDataCacheValid();
	const FInputActionKeyMapping Mapping = FInputActionKeyMapping(NAME_None, InKeyEvent.GetKey(),
		InKeyEvent.IsShiftDown(), InKeyEvent.IsControlDown(), InKeyEvent.IsAltDown(), InKeyEvent.IsCommandDown());

	return InputConfigureGroupDataCache->BackAction.Contains(Mapping);
}


void InputConfigureLibrary::BindChangedInputGroupEvent(FOnChangedInputGroup inChangedInputGroup)
{
	EnsureDataCacheValid(false);
	InputConfigureGroupDataCache->BindChangedInputGroupEvent(inChangedInputGroup);
}

void InputConfigureLibrary::BindAnyKeyDownEvent(FOnAnyKeyDown inAnyKeyDownEvent)
{
	EnsureDataCacheValid(false);
	InputConfigureGroupDataCache->BindAnyKeyDownEvent(inAnyKeyDownEvent);
}

void InputConfigureLibrary::CallAnyKeyDownEvent(const FKeyEvent& InKeyEvent, bool bIsDown)
{
	EnsureDataCacheValid(false);
	InputConfigureGroupDataCache->CallAnyKeyDownEvent(InKeyEvent, bIsDown);
}


